#include <ctype.h>
#include <stdio.h>
#include <assert.h>
#include <stack>
#include <string.h>
#include "Element.hpp"
#include "String.hpp"
#include "Parser.hpp"
#include "Text.hpp"

using namespace xml;

stack<String> tag_stack;
vector<st_lookup> lookup;

size_t hash_pre = 2; 


Parser::Parser(){

}

// TODO : Reset when file hit and end;
size_t cnt = 0;
short start = 0;

enum state{
	WHITESPACE = 1,
	STRING,
	NUMBER,
	LESS,
	MORE,
	FSLASH,
	COLON,
	EQUAL,
	TEXT,
	QUOTES,
	URI,
	END
} state ;

	
char * recursive_parse(char *ch, size_t sz, Element *ele){

	String str, child_nm, nm;
	int num;
	short end_tag_flag = 0, no_nm = 0, start_tag_flag = 0;
	short xmlns_flag = 0, c_nm_flag = 0;
	while (state != END) {
		
		switch (state) {
			
			case LESS: 
				{
					start_tag_flag = 1;
					//nm.clear();
					if (isalpha(*ch)) {
						state = STRING;
						str += *ch;
					} else if (*ch == '/'){
						state = FSLASH;
						end_tag_flag = 1;	
						no_nm = 1;
					}	
				}
				break;

			case MORE:
				{
					String tmp = ele->nmspace();
					if (strcmp (tmp.c_str(), "") != 0){
						int i;
						for ( i = lookup.size() - 1; i >= 0; i--){
							if (strcmp (lookup[i].nm.c_str(), tmp.c_str()) == 0){
								ele->addNMSpace(lookup[i].value);
								break;
							}	
						}
					}
					if (*ch == '<')
						state = LESS;
					else if (*ch == EOF)
						state = END;
					else{	
						state = TEXT;
						str.clear();
						str = *ch;
					}	
				}
				break;

			case FSLASH:
				{
					if(isspace(*ch)){
						state = WHITESPACE;
					}else if (isalpha (*ch)){
						state = STRING;
						str += *ch;
					}

				}	
				break;

			case TEXT:
				{
					if (*ch == '<'){
						state = LESS;
						Text *txt = new Text;
						txt->addText (str.c_str());
						ele->addNode (txt);
						str.clear();
					}	
					else{
						state = TEXT;
						str += *ch;
					}	
				}
				break;

			case COLON:
				{
					if (isalpha(*ch)){
						state = STRING;
						str = *ch;
					} 
					else{
						printf ("WWWWWWWWWWWWWWWWWWWWWWW In COLON assert : %c\n", *ch);
						assert(false);
					}
				}
				break;

			case URI:
				{
					if (*ch == '"'){
						state = QUOTES; 				// TODO: Handle the Quite string.
						if (c_nm_flag){
							ele->addChildNMSpace (child_nm,str);			// TODO : Add value of ns to somewhare
							st_lookup stl ;
							stl.value.assign(str);
							stl.nm.assign(child_nm);
							lookup.push_back(stl);
							child_nm.clear();
							c_nm_flag = 0;
						}

						str.clear();
					} else {	
						state = URI;
						str += *ch;
					}
				}
				break;
			
			case QUOTES:
				{
					if (*ch == '"'){
						state = URI;
					} else if (*ch == '>'){
						state = MORE;
						str.clear();
					} else if (isspace(*ch)){
						state = WHITESPACE;
					} else if (isalpha(*ch))	
						state = STRING;
				}	
				break;
			
			case EQUAL:
				{
					if (*ch == '"')
						state = URI;
					else{
						printf ("Bad XML : EQUAL Assert : %c\n",*ch);
						assert(false);					
					}	
				}
				break;

			case WHITESPACE:
				{
					if (isalpha(*ch)) {
						state = STRING;
						str = *ch;
					} else if (isdigit(*ch)) {
						state = NUMBER;
						str = *ch;
						num = *ch - '0';
					} else if (*ch == '\0') {
						state = END;
					} else if (*ch == '<') {
						state = LESS;
					} else if (*ch == '>'){
						state = MORE;
						if (end_tag_flag){
							end_tag_flag = 0;
						}	
						else	
							str.clear();
					}
				}
				break;
			case STRING:
				{
					if (isalnum(*ch)) {
						str += *ch;
					} else if (*ch == '_'){
						str += *ch;
						state = STRING;				
					}else if (isspace(*ch)) {
						state = WHITESPACE;
						if (end_tag_flag){
							end_tag_flag = 0;
							String tmp = str + nm;
							
							if (tmp.compare(tag_stack.top()) != 0){
								cout << " Bad xml file " << endl;
								cout << " Bad xml file HHHHHHHHH :  " << tmp.c_str() << " : " << tag_stack.top().c_str() <<   endl ;
								assert(false);
							}
							tag_stack.pop();

							for (int i = 0; i < ele->nm_child(); i++){
								lookup.pop_back();
							}
							nm.clear();
							str.clear();
							return ch;
						}
						else{
							// Create and add new element
							if (start){ 
								ele->addName(str);
								tag_stack.push (str);
								start = 0;
								no_nm = 1;
							}else{
								Element *new_ele = new Element;			//TODO: Free This
								if (start_tag_flag){
									new_ele->addNMSpace(nm);
									nm.clear();
									start_tag_flag = 0;
								}
								new_ele->addName (str);
								tag_stack.push (new_ele->name() + new_ele->nmspace());
								ch++;
								
								ch = recursive_parse (ch, sz, new_ele);
								ele->addNode (new_ele);
							}

						}	
						str.clear();
					} else if (*ch == '<') {
						state = LESS;
						Text *txt = new Text;
						txt->addText (str.c_str());
						ele->addNode (txt);
						str.clear();
					} else if (*ch == '\0') {
						state = END;
						str.clear();
					} else if (*ch == ':'){
						// Add namespace tag here
						if (start_tag_flag || end_tag_flag){
							nm.clear();
							nm.assign(str);
						}
						if (strcmp ("xmlns",str.c_str()) == 0){
							xmlns_flag = 1;
						}	
						else{	
							if (!no_nm){
								if (!start_tag_flag){	
									ele->addNMSpace (str);			// TODO : Add value of ns to somewhare
								}
								no_nm = 0;			
							}
							if (end_tag_flag)
								no_nm = 0;
						}	
						state = COLON;
						str.clear();
				} else if (*ch == '='){
					state = EQUAL;
					if (xmlns_flag){
						// Add namespace tag here
							c_nm_flag = 1;
							child_nm.assign(str);
							xmlns_flag = 0;
						}
						str.clear();
					} else if (*ch == '>'){
						state = MORE;
						if (end_tag_flag){
							String tmp;;
							end_tag_flag = 0;
							tmp = str + nm;
							
							if (tmp.compare(tag_stack.top()) != 0){
								cout << " Bad xml file HHHHHHHHH :  " << tmp << " : " << tag_stack.top() <<   endl ;
								assert(false);
							}
							tag_stack.pop();
							
							for (int i = 0; i < ele->nm_child(); i++){
								st_lookup st = lookup.back();
								lookup.pop_back();
							}
							nm.clear();
							str.clear();
							return ch;
						}	
						else{	
							// Create and add new element
							if (start){
								ele->addName(str);
								tag_stack.push (str);
								start = 0;
							}else{
								Element *new_ele = new Element;			//TODO: Free This
								if (start_tag_flag){
									new_ele->addNMSpace(nm);
									nm.clear();
									start_tag_flag = 0;
								}
								new_ele->addName(str);
								
								tag_stack.push (new_ele->name() + new_ele->nmspace());
								ch++;
								ch = recursive_parse(ch, sz, new_ele);
								ele->addNode (new_ele);
							}
						}	
						str.clear();
					} else if (*ch == EOF){
						state = END;
					} else if(cnt == sz){
						state = END;
					}else {

						printf ("String Assert : %c\n", *ch);
						assert(false);
					}
				}
				break;
			case NUMBER:
				{
					if (isalpha(*ch)) {
						state = STRING;
						str += *ch;
					} else if (isdigit(*ch)) {
						str += *ch;
						num = 10*num + (*ch - '0');
					} else if (isspace(*ch)) {
						state = WHITESPACE;
					} else if (*ch == '\0') {
						state = END;
					} if (*ch == EOF){
						state = END;
					}else {
						printf ("Number Assert : %c\n", *ch);
						assert(false);
					}
				}
				break;
			default:
				assert(false);
		}
		ch++;
		cnt++;
		if (cnt == sz){
			state = END;
			str.clear();
		}
	}
	return NULL; // This is an EOF
}


const Element * Parser::parse (const char *doc, size_t sz){
	
	Element *ele = new Element;
	char *ch = const_cast<char *> (doc);
	state = WHITESPACE;
	start = 1;
	recursive_parse(ch, sz, ele);
	
	return ele;
	
}
